#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QtOpenGL>

class Entity
{
public:
	Entity();
	~Entity();
public:
	virtual void init() = 0;
	virtual void draw(QMatrix4x4 &viewMat, QMatrix4x4 &projMat, QVector3D &lightPos) = 0;
};

class StlEntity : public Entity
{
public:
	StlEntity();
	~StlEntity();

public: // inherited from Entity
	void init();
	void draw(QMatrix4x4 &viewMat, QMatrix4x4 &projMat, QVector3D &lightPos);

public:
	void loadFromFile(QString sFileName);

	void calculateSurfaceArea();
	float getSurfaceArea();
	void calculateVolume();
	float getVolume();

	void TranslateX(float x) { m_OffX += x; };
	void TranslateY(float y) { m_OffY += y; };
	void TranslateZ(float z) { m_OffZ += z; };
	void RotateX(float x) { m_RotX += x; };
	void RotateY(float y) { m_RotY += y; };
	void RotateZ(float z) { m_RotZ += z; };

	float GetModelMaxX() { return m_XMax; }
	float GetOffX() { return m_OffX; }
	float GetScale() { return m_Scale; }
	//float GetModelMinXScaled() { return m_XMin * m_Scale; }
	float GetModelMaxXScaled() { return m_XMax * m_Scale; }
	float GetWorldMinX() { return (m_XMin*m_Scale) + m_OffX + m_PosX; }
	float GetWorldMaxX() { return (m_XMax*m_Scale) + m_OffX + m_PosX; }
	float GetWorldMaxY() { return (m_YMax*m_Scale) + m_OffY + m_PosX; }
	float GetWorldMaxZ() { return (m_ZMax*m_Scale) + m_OffZ + m_PosX; }
	float GetWidth()  { return m_XMax - m_XMin; }
	float GetHeight() { return m_YMax - m_YMin; }
	float GetDepth()  { return m_ZMax - m_ZMin; }
	float GetWidthScaled()  { return this->GetWidth()  * m_Scale; }
	float GetHeightScaled() { return this->GetHeight() * m_Scale; }
	float GetDepthScaled()  { return this->GetDepth()  * m_Scale; }

private: // model
	QOpenGLShaderProgram m_Program;
	QVector<QVector3D> m_Positions;
	QVector<QVector3D> m_Normals;
	float m_XMin, m_XMax, m_YMin, m_YMax, m_ZMin, m_ZMax;
	float m_SurfaceArea;
	float m_Volume;
private: // model to world
	QMatrix4x4 m_MatModel;
	float m_PosX, m_PosY, m_PosZ; // center model
	float m_OffX, m_OffY, m_OffZ;
	float m_RotX, m_RotY, m_RotZ;
	float m_Scale;
};

// polar coordinates (spherical coordinates)
//TODO: should this be named ArcBallCamera?
class OrbitalCamera
{
public:
	OrbitalCamera();
	~OrbitalCamera();
public:
	void setLookAtPt(QVector3D lookAt);
	void translateLookAtPt(QVector3D delta);
	void setProj(QMatrix4x4 proj);
	QMatrix4x4 &getViewMat();
	QMatrix4x4 &getProjMat();
	void rotateTheta(float radians);
	void rotatePhi(float radians);
	void increaseRadius(float delta);
	QVector3D getEyePos() { return m_EyePos; }
private:
	QMatrix4x4 m_ViewMat;
	QMatrix4x4 m_ProjMat;
private:
	QVector3D m_EyePos;
	QVector3D m_LookAtPt;
	float m_Radius; // distance from look-at point
	float m_Theta; // azimuth angle
	float m_Phi; // altitude angle (polar angle)
	const float MIN_RADIUS = 25.0f;
};

class GLWidget : public QGLWidget
{
	Q_OBJECT
public:
	GLWidget(QWidget *parent) : QGLWidget(parent) { m_LightPos = QVector3D(0,0,200); }
	GLWidget() {};
	~GLWidget() {};
public:
	void addEntityFromFile(QString sFileName, float &surfaceArea, float &volume);
private: // inherited from QGLWidget, for opengl
	void initializeGL();
	void paintGL();
	void resizeGL(int width, int height);
private: // inherited, mouse input, keyboard input
	void mousePressEvent(QMouseEvent *event);
	void mouseMoveEvent(QMouseEvent *event);
	void wheelEvent(QWheelEvent *event);
public:
	void keyPressEventHandler(QKeyEvent *event);
	void keyReleaseEventHandler(QKeyEvent *event);
private:
	QList<StlEntity*> m_Entities;
	QPoint m_LastPos; // mouse
private:
	QVector3D m_LightPos;
private:
	OrbitalCamera m_OrbCam;
};

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
	Q_OBJECT

public:
	explicit MainWindow(QWidget *parent = 0);
	~MainWindow();

private slots:
	void on_action_Open_triggered();
	void on_actionE_xit_triggered();

private:
	void keyPressEvent(QKeyEvent *event);
	void keyReleaseEvent(QKeyEvent *event);

private:
	Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H
